<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>数据库管理</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" href="/static/css/bootstrap.css">
    <script src="/static/js/jquery.js"></script>
    <script src="/static/js/vue.js"></script>
  </head>
  <body>
    <nav class="navbar navbar-default" id="app">
      <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="/">数据库管理</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
          <ul class="nav navbar-nav">
            <li><a href="/conn/index">连接 <span class="sr-only">(current)</span></a></li>
            <li class="active"><a href="/dto/index">Dtos</a></li>
            <li><a href="/dataset/index">训练数据集</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="#">登录</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">系统菜单 <span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="#">Action</a></li>
                <li><a href="#">Another action</a></li>
                <li><a href="#">Something else here</a></li>
                <li role="separator" class="divider"></li>
                <li><a href="#">Separated link</a></li>
              </ul>
            </li>
          </ul>
        </div><!-- /.navbar-collapse -->
      </div><!-- /.container-fluid -->

      <div class="container-fluid">
        <div class="row">
          <div class="col-sm-3 left-menu">
            <div>
              <!-- 数据库连接 -->
              <ol class="breadcrumb">
                <li><a href="javascript:void(0)">[[ modelDb.name ]]</a></li>
                <li><a href="javascript:void(0)">[[ modelDb.dbtype ]]</a></li>
                <li v-if="modelDb.dbname"><a href="javascript:void(0)">[[ modelDb.dbname ]]</a></li>
                <li><a href="javascript:void(0)" @click="handleChangeDb">更改</a></li>
              </ol>
            </div>
            <div class="db-tables">
              <table class="table table-bordered">
                <thead>
                  <tr>
                    <th>序号</th>
                    <th>名称</th>
                  </tr>
                </thead>
                <tbody>
                  <tr v-for="(item,index) in list" :key="index" :style="{backgroundColor:currentTable == index ?'#b7e7d7':'',cursor:'pointer'}" @click="handleTableChange(item,index)">
                    <td>[[ index + 1 ]]</td>
                    <td>[[ item.name ]]</td>
                  </tr>
                </tbody>
              </table>
            </div>
          </div>
          <div class="col-sm-9 center-container">
            <ul class="nav navbar-nav">
              <li :class="[currentTab == index?'active':'']" v-for="(item,index) in listTabs" :key="index" @click="currentTab=index">
                <a href="javascript:void(0)">[[ item ]] </a>
              </li>
            </ul>
            <div v-if="currentTab == 0" style="height: calc(100% - 50px); overflow: hidden; width: 100%;">
              <div style="height: calc(100% - 50px); overflow-y: scroll; width: 100%;">
                <table class="table table-bordered">
                  <thead>
                    <tr>
                      <th>序号</th>
                      <th>名称</th>
                      <th>类型</th>
                      <th>可空</th>
                      <th>长度</th>
                      <th>小数位</th>
                      <th>主键</th>
                      <th style="width: 50%;">描述</th>
                      <th style="width: 200px; text-align: center;">操作</th>
                    </tr>
                  </thead>
                  <tbody style="height: calc(100% - 145px); overflow-y: scroll;">
                    <tr v-for="(item,index) in listCols" :key="index" :style="{backgroundColor:currentCol == index ?'#b7e7d7':''}" @click="handleColChange(item,index)">
                      <td>[[ index + 1 ]]</td>
                      <td>[[ item.name ]]</td>
                      <td>[[ item.col_type ]]</td>
                      <td>[[ item.nullable ]]</td>
                      <td>[[ item.len ]]</td>
                      <td> --</td>
                      <td>[[ item.pk ]]</td>
                      <td>
                        <input type="text" class="form-control" v-model="item.desc" placeholder="输入字段描述">
                      </td>
                      <td style="text-align: center;">
                        <button type="button" class="btn btn-default btn-sm" @click="handleCopyProps(item)">复制属性</button>
                        <button type="button" class="btn btn-primary btn-sm" @click="handleUpdateDesc(item,$event)">更新注释</button>
                      </td>
                    </tr>
                  </tbody>
                </table>

              </div>
              <div class="footer-options">
                <button type="button" class="btn btn-primary" @click="handleFanyiCols">AI 注释</button>
              </div>
            </div>
            <div v-if="currentTab == 1" class="dto-content">
              <textarea class="form-control" v-model="dtoContent"></textarea>
              <div class="footer-options">
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.attrSqlSugar" @change="handleRebuild" /> SqlSugar
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.attrColum" @change="handleRebuild" /> 指定列名
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.firstLow" @change="handleRebuild" /> 首字母小写
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.firstUpper" @change="handleRebuild" /> 首字母大写
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.removeLine" @change="handleRebuild" /> 去除下划线
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.removeAttr" @change="handleRebuild" /> 去除F_[name]
                </label>
              </div>
            </div>
            <div v-if="currentTab == 2" class="dto-content">
              <textarea class="form-control" v-model="jsObject.content"></textarea>
              <div class="footer-options">
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.firstLow" @change="handleRebuild" /> 首字母小写
                </label>
                <label class="checkbox-inline">
                  <input type="checkbox" v-model="settings.descTop" @change="handleRebuild" /> 注释在上
                </label>
              </div>
            </div>
            <div v-if="currentTab == 3" class="ai-content">
              <textarea class="form-control" v-model="assistant.content"></textarea>
              <div class="footer-options">
                <div class="input-group">
                  <span class="input-group-addon" id="basic-addon1">@ AI</span>
                  <input type="text" class="form-control" v-model="assistant.message" placeholder="输入你的问题">
                  <span class="input-group-addon" style="cursor: pointer;" @click="handleSendAIMsg">发送</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="modal fade" tabindex="-1" role="dialog" id="formModal">
        <div class="modal-dialog" role="document">
          <div class="modal-content">
            <div class="modal-header">
              <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
              <h4 class="modal-title">更改连接</h4>
            </div>
            <div class="modal-body">
              <table class="table table-bordered">
                <thead>
                  <tr>
                    <th>序号</th>
                    <th>名称</th>
                  </tr>
                </thead>
                <tbody>
                  <tr style="cursor: pointer;" v-for="(item,index) in listDb" :key="index" @click="handleSelectDb(item,index)" :class="[currentDb == index?'active':'']">
                    <td>[[ index+1 ]]</td>
                    <td>
                      <span>[[ item.name ]]</span>
                      <span>/[[ item.dbtype ]]</span>
                      <span v-if="modelDb.dbname">/[[ item.dbname ]]</span>
                    </td>
                  </tr>
                </tbody>
              </table>
            </div>
            <div class="modal-footer">
              <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
              <button type="button" class="btn btn-primary" @click="saveDbChanges">确定</button>
            </div>
          </div><!-- /.modal-content -->
        </div><!-- /.modal-dialog -->
      </div><!-- /.modal -->

    </nav>


    <script src="/static/js/bootstrap.js"></script>
    <script type="text/javascript">
      new Vue({
        el: '#app',
        delimiters: ["[[", "]]"],
        data() {
          return {
            listDb: [],
            list: [],
            listCols: [],
            listTabs: ['字段列表', '实体类', 'JSON对象', 'AI生成'],
            currentTab: 0,
            currentDb: 0,
            currentTable: 0,
            currentCol: 0,
            dtoContent: '',
            settings: {
              attrSqlSugar: false,
              attrColum: false,
              firstLow: false,
              firstUpper: false,
              removeLine: false,
              removeAttr: true,
              descTop: false,
            },
            assistant: {
              message: '你好，你是谁？',
              content: ''
            },
            jsObject: {
              content: ''
            },
            modelDb: {
              dbname: '',
              name: '',
              dbtype: ''
            }
          }
        },
        created() {
          this.fetchData()
        },
        methods: {
          fetchData() {
            fetch('/conn/list').then(async response => {
              let data = await response.json()
              this.listDb = data
              if (data.length > 0) {
                this.modelDb = data[0]
                this.fetchTables(this.modelDb.id)
                // console.log(data)
              }
            })
          },
          fetchTables(id) {
            fetch('/conn/tables?id=' + id).then(async response => {
              let data = await response.json()
              this.list = data
              // console.log(data)
            }).catch(err => {
              console.log(err)
            })
            // console.log(id)
          },
          fetchTableColums(id, name) {
            fetch('/conn/colums', {
              headers: { "content-type": "application/json" },
              method: "POST",
              body: JSON.stringify({ id, name })
            }).then(async response => {
              let data = await response.json()
              this.listCols = data
              this.columsToProps()
              this.handleJsObject()
              // console.log(data)
            }).catch(err => {
              console.log(err)
            })
            // console.log(id)
          },
          handleRebuild() {
            this.columsToProps()
            this.handleJsObject()
          },
          columsToProps() {
            let content = ''
            for (let col of this.listCols) {

              let col_desc = col.desc
              let desc = `\t/// <summary>\n\t/// ${col_desc??''}\n\t/// </summary> \n`
              let type = this.getPropType(col)
              let attr = ''
              if (this.settings.attrSqlSugar) {
                attr = this.getPopAttr(col)
              }
              let name = this.getcolumnname(col.name)
              content += `${desc} ${attr}\t public ${type}  ${name} { get; set; }\n\n`
            }
            this.dtoContent = content
          },
          handleJsObject() {
            let content = '{\n'
            for (let col of this.listCols) {
              let col_desc = col.desc

              let type = this.getPropType(col)
              let defval = `''`
              if (['int', 'long', 'decimal'].includes(type)) {
                defval = 0
              }
              // console.log(type)
              let name = this.getcolumnname(col.name)

              if (this.settings.descTop) {
                content += ` // ${col.desc}\n ${name}:${defval},  \n`
              } else {
                content += `  ${name}:${defval}, // ${col.desc}\n`
              }
            }
            content += ` }\n`
            // console.log(content)
            this.jsObject.content = content
          },
          getcolumnname(name) {
            if (this.settings.removeAttr && (name.startsWith('f_') || name.startsWith('F_'))) {
              name = name.substr(2)
            }
            if (this.settings.removeLine) {
              let words = name.split('_')
              let keys = words.map(t => {
                let first = t.at(0).toUpperCase()
                return `${first}${t.substr(1)}`
              })
              name = keys.join('')
            }
            if (this.settings.firstUpper) {
              let first = name.at(0).toUpperCase()
              return `${first}${name.substr(1)}`
            }

            if (this.settings.firstLow) {
              let first = name.at(0).toLowerCase()
              return `${first}${name.substr(1)}`
            }
            return name

          },
          getPropType(col) {
            let list = [
              { key: "varchar", value: "string" },
              { key: "nvarchar", value: "string" },
              { key: "bytea", value: "byte[]" },
              { key: "int", value: "int" },
              { key: "bigint", value: "long" },
              { key: "int4", value: "int" },
              { key: "timestamp", value: "DateTime" },
              { key: "datetime", value: "DateTime" },
              { key: "INTEGER", value: "int" },
              { key: "DATE", value: "DateTime" },
              { key: "image", value: "byte[]" },
              { key: "text", value: "string" }
            ]
            for (let obj of list) {
              if (obj.key.toUpperCase() == col.col_type.toUpperCase()) {
                return obj.value
              }
            }
            return 'string'
            // console.log(col)
          },
          getPopAttr(col) {
            let attr = `\t[SugarColumn(IsNullable = true`
            if (typeof(col.len) == 'number') {
              attr += `,Length =${col.len}`
            } else if (col.len && col.len.includes(',')) {
              //DecimalDigits =
              let keys = col.len.split(',')
              attr += `,Length =${keys[0]},DecimalDigits =${keys[1]}`
            } else if (col.len && col.len != '-1') {
              attr += `,Length =${col.len}`
            }
            if (this.settings.attrColum) {
              attr += `,ColumnName ="${col.name}"`
            }
            return attr + ')]\n'
          },
          handleChangeDb() {
            $('#formModal').modal('show')
          },
          saveDbChanges() {
            let index = this.currentDb
            this.modelDb = this.listDb[index]
            $('#formModal').modal('hide')
            this.fetchTables(this.modelDb.id)
          },
          handleSelectDb(obj, index) {
            this.currentDb = index
            // console.log(obj)
          },
          handleTableChange(item, index) {
            this.currentTable = index
            this.fetchTableColums(this.modelDb.id, item.name)
            // console.log(obj)
          },
          handleColChange(item, index) {
            this.currentCol = index
          },
          handleCopyProps(item) {
            let type = this.getPropType(item)
            let desc = item.desc ?? ''
            let content = `\t/// <summary>\n\t/// ${desc}\n\t/// </summary> \n\t public ${type}  ${item.name} { get; set; }`
            navigator.clipboard.writeText(content)
            // console.log(item)
          },
          handleSendAIMsg() {
            let content = this.assistant.message
            this.assistant.message = ''
            this.assistant.content += `\n我 => ${content}`
            fetch('/ai/chat', {
              headers: { "content-type": "application/json" },
              method: "POST",
              body: JSON.stringify({ token: '101010010', content })
            }).then(async response => {
              if (!response.ok) {
                throw new Error('Network response was not ok');
              }
              return response.body
              let data = await response.json()
              this.assistant.content += `\nAI => ${data.content}`
              // console.log(data)
            }).then(stream => {
              const reader = stream.getReader();
              const decoder = new TextDecoder('utf-8');

              let readChunk = () => {
                reader.read().then(({ value, done }) => {
                  if (done) {
                    // 数据流结束
                    console.log('Done reading the stream');
                    return;
                  }
                  let text = decoder.decode(value)
                  this.assistant.content += text
                  // console.log(text)
                  setTimeout(readChunk, 1000); // 延时1秒后继续读取下一个数据块
                });
              }

              readChunk();
            }).catch(err => {
              console.log(err)
            })
          },
          handleFanyiCols({ target }) {
            if (this.listCols.length == 0) {
              return
            }
            let cols = this.listCols.map(t => t.name)
            let content = `表字段${cols.join('、')}分别表示什么意思？`
            target.innerText = "等待AI回复中"
            fetch('/ai/chat', {
              headers: { "content-type": "application/json" },
              method: "POST",
              body: JSON.stringify({ token: '101010010', content })
            }).then(async response => {
              if (!response.ok) {
                throw new Error('Network response was not ok');
              }
              let data = await response.json()
              for (let key in data) {
                let col = this.listCols.find(t => t.name == key)
                if (col) {
                  col.desc = data[key]
                }
              }
              // console.log(data)
            }).catch(err => {
              console.log(err)
            }).finally(() => {
              target.innerText = "AI 注释"
            })
          },
          handleUpdateDesc(obj, { target }) {

            target.innerText = '更新中'
            let table = this.list[this.currentTable]
            let model = { id: this.modelDb.id, table_name: table.name, col_type: obj.col_type, name: obj.name, desc: obj.desc }
            // console.log( model )
            // return
            fetch('/conn/columndesc', {
              headers: { "content-type": "application/json" },
              method: "POST",
              body: JSON.stringify(model)
            }).then(async response => {
              if (!response.ok) {
                throw new Error('Network response was not ok');
              }
              let data = await response.json()
              target.innerText = '更新注释'
              console.log(data)
            }).catch(err => {
              console.log(err)
            }).finally(() => {
              target.innerText = '更新注释'

            })
          }
        }
      })
    </script>
  </body>
  <style>
    .row {
      height: calc(100vh - 75px);

    }

    tr .active td {
      cursor: pointer;
      background-color: #096;
    }

    .left-menu {
      height: 100%;
      background-color: #FFF;
      border: 1px solid #EEE;
      box-sizing: border-box;
      padding: 0px;
      overflow: hidden;
    }

    .center-container {
      box-sizing: border-box;
      height: 100%;
      overflow-y: scroll;
      background-color: #FFF;
      border: 1px solid #EEE;
      padding: 0px;
      overflow: hidden;
    }

    .breadcrumb {
      margin: 0px;
    }

    .db-tables {
      height: calc(100% - 50px);
      overflow-y: auto;
    }

    .dto-content {
      height: 100%;
      width: 100%;

      textarea {

        height: calc(100% - 100px);
        width: 100%;
      }
    }

    .footer-options {
      position: relative;
      border-top: 1px solid #EEE;
      bottom: 0px;
      padding: 10px;
    }

    .ai-content {
      height: 100%;
      width: 100%;
    }

    .ai-content textarea {

      height: calc(100% - 120px);
      width: 100%;

    }
  </style>
</html>